home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Ken Long / MissileCmd2.3-p-c / Think P / Missile.p next >
Encoding:
Text File  |  1994-12-04  |  39.3 KB  |  1,239 lines  |  [TEXT/PJMM]

  1. {From: mrob @ world . std . com ( Robert P Munafo )}
  2. { Subject : "Missile " source from 1984}
  3.  
  4. {Recently in comp . sys . mac . programmer there has been some discussion }
  5. {of early Macintosh programs that still run on current machines , }
  6. {and my Missile Command game from mid - 1984 was mentioned .}
  7.  
  8. {This program was written in Lisa Pascal, }
  9. {the cross - development system that everyone had to use}
  10. {until Stanford's sumacc came along,}
  11. {and has the rather charming property of continuing to run on}
  12. { all new machines that Apple comes out with .}
  13. { I ran it on my own Power Mac 6100 / 60 yesterday just for fun .}
  14. { It 's just as playable as it was on the old 128K Mac}
  15. { ( although the timing for the " Game Over " screen is now way off ,}
  16. { as I didn 't check TickCount for that ) .}
  17.  
  18. {I have not done anything to this code ,}
  19. { like try to port it to any recent Pascal compiler or anything like that . }
  20. {I know that Lisa Pascal , Macintosh Pascal , }
  21. {and TML Language Systems Pascal were all reasonably }
  22. {compatible with each other;}
  23. {after that I switched over to Think C and lost track .}
  24.  
  25. {The "%title " and " %subtitle " directives were}
  26. {for the print spooler on the Dartmouth mainframe that I used to }
  27. {print out listings .}
  28.  
  29. {There seem to be a lot more options in the Game Options dialog}
  30. { than the actual Missile game provides .}
  31. { I 'm not sure what' s going on with that .}
  32. { - - - - - - - - - - - Cut Here - - - Pascal Source code - - - - - - - - - - -}
  33.  
  34.  
  35. { %title 'Missile - a game for the Apple Macintosh.' }
  36. { %subtitle 'Overall program description.' }
  37. { $X- }
  38.   { Turns off A.R.S.E. }
  39.  
  40. program Game;
  41. {}
  42. { Missile -- A Missile Command game for the Macintosh}
  43. {            by Robert P. Munafo at Dartmouth College.}
  44. {}
  45. {   I wrote this to learn a bit about Mac programming.  It has a}
  46. { few 'standard user interface' bugs:}
  47. {}
  48. {   It doesn't update the window during the 'End of round X' and}
  49. {     'GAME OVER' routines.  (It only updates while playing.)}
  50. {   It won't quit when you select "quit" while a desk accessory is}
  51. {     on screen.  Instead, it will wait until you close all the}
  52. {     desk accessories, then quit.}
  53. {   The cursor looks the same all the time.  It should change}
  54. {     shape to let the user know when he can and cannot fire missiles.}
  55. {}
  56. { It also has some missing features (which I hope to add soon):}
  57. {}
  58. {   o Smart Bombs}
  59. {   o Add an "Ammunition: [xxx]% of normal" item to the dialog box.}
  60. {   o Sound}
  61. {   o User should be able to pick what round the game starts with}
  62. {   o High scores list saved on the disk}
  63. {   o Two-player option}
  64. {   o The animation needs to be made faster (in some manner)}
  65. {}
  66.  
  67. { %subtitle 'USES statements' }
  68.     uses
  69.         QuickDraw, OSUtils, ToolUtils, Packages;
  70.  
  71. { %subtitle 'Constants' }
  72.     const
  73.         lastMenu = 4;      { number of menus }
  74.         appleMenu = 1;     { menu ID for desk accessory menu }
  75.         fileMenu = 256;    { menu ID for File menu }
  76.         EditMenu = 257;    { menu ID for the Edit menu. }
  77.         GameMenu = 258;    { menu ID for Game menu }
  78.         str_1_ID = 300;    {ID for my first string of text}
  79.         str_2_ID = 350;    {ID for my second string of text}
  80.         box_ID = 450;      {ID of "about" dialog box}
  81.         box2_id = 451;     {ID of options box}
  82.  
  83.         fbIdle = 0;
  84.         fbGrow = 1;
  85.         fbShrink = 2;
  86.         maxfb = 60;        { Maximum number of fireballs allowed. }
  87.         fbRad = 15;        { Radius of fireballs. }
  88.         fbRadRate = 2;     { Rate of expansion/contraction of fireballs. }
  89.         msIdle = 0;
  90.         msActive = 1;
  91.         msNormal = 1;
  92.         msMirv = 2;
  93.         msSmart = 3;
  94.         maxms = 20;        { Maximum number of missiles allowed. }
  95.         mWidth = 3;        { Width of missile tracks. }
  96.         CityHeight = 40;   { Distance from bottom up to cities. }
  97.         CityWidth = 50;    { Width of cities. }
  98.         BuildHeight = 30;  { Maximum height of buildings. }
  99.         BuildWidth = 4;    { Width of buildings. }
  100.         nCities = 6;       { Number of cities. }
  101.  
  102.     type
  103.         fireball = record    { Data type to describe a fireball. }
  104.                 bounds: Rect;    { Rectanggle to copy bits into. }
  105.                 size: INTEGER;   { Current radius of fireball. }
  106.                 mode: INTEGER;   { Idle, Growing, or shrinking. }
  107.             end;
  108.  
  109.         missile = record    { Data type to describe a missile. }
  110.                 start: Point;    { One endpoint of the missile's path. }
  111.                 pos: Point;      { Position within path - 0 to 1. }
  112.                 dh, dv: INTEGER; { Vertical and horizontal speed in pixels. }
  113.                 city: INTEGER;   { Number of the target city. }
  114.                 kind: INTEGER;   { MIRV or normal. }
  115.                 mode: INTEGER;   { Idle or Active. }
  116.             end;
  117.  
  118.     var
  119.         myMenus: array[1..lastMenu] of MenuHandle;
  120.         screenRect: Rect;
  121.         doneFlag: BOOLEAN;
  122.         myEvent: EventRecord;
  123.         code, refNum: INTEGER;
  124.         wRecord: WindowRecord;
  125.         myWindow, whichWindow: WindowPtr;
  126.         first_string, second_string: StringHandle; { Handles to strings of text for AboutMissile. }
  127.  
  128.     { My game variables are here. }
  129.         fbBitMap: BitMap;      { BitMap for fireball pictures. }
  130.         fbBits: array[1..3600] of INTEGER;  { Actual bits for fireball pictures. }
  131.  
  132.         crosshairs: Cursor;    { Crosshairs cursor read in from resource. }
  133.         crosshairs2: Cursor;
  134.         hCurs, hCurs2: CursHandle;    { Handle to the cursor. }
  135.         CityPattern: PatHandle;{ Handle to pattern in which to draw cities. }
  136.         FieldWidth: INTEGER;   { Width of the playing field. }
  137.         FieldHeight: INTEGER;  { Height of the playing field. }
  138.         i: INTEGER;            { Loop variable. }
  139.         fbs: array[1..maxfb] of fireball;     { Each entry describes one fireball. }
  140.         missiles: array[1..maxms] of missile; { Each entry describes one missile. }
  141.         nMissiles: INTEGER;    { Number of currently active missiles. }
  142.         lastTick: LONGINT;     { System clock at time of previous update. }
  143.         gameOver: BOOLEAN;     { Set true when all cities are destroyed. }
  144.         PauseFlag: BOOLEAN;    { True when game is being paused. }
  145.         Playing: BOOLEAN;      { True except buring bonus points and GAME OVER screens. }
  146.         esFlag: BOOLEAN;       { True if "GAME OVER" is to be drawn after exiting main loop. }
  147.         enddelay: INTEGER;     { Used to wait a while after end of round. }
  148.         citiesLeft: INTEGER;   { Number of cities still alive. }
  149.         bcities: INTEGER;      { Number of bonus cities they have left. }
  150.         cities: array[1..nCities] of BOOLEAN; { State of each city : false = destroyed. }
  151.         cities2: array[1..nCities] of BOOLEAN; { State of the city before the round began. }
  152.         nukeHeight: INTEGER;   { Vertical position of fireball required to destroy city. }
  153.         endv: INTEGER;         { Y-coordinate of the end of a missile's path. }
  154.         MirvRate: INTEGER;
  155.         mirvHeight: INTEGER;   { Height at which Mirvs MIRV. }
  156.         mirvNasty: INTEGER;    { Percent chance of sub-missile on mirv for each city. }
  157.         msSpeed: INTEGER;      { Vertical speed of missiles, in 1/16ths of a pixel.}
  158.         msRate: INTEGER;       { Percent rate of missile production. }
  159.         RoundNumber: INTEGER;  { Number of the current round. }
  160.         Score: LONGINT;        { The player's current score. }
  161.         DisScore: LONGINT;     { Score currently displayed on the screen. }
  162.         HighScore: LONGINT;    { The current highest score. }
  163.         RoundH: INTEGER;
  164.         Scoreh: INTEGER;       { Horiz. position of text 'Score:' }
  165.         ScoreNumH: INTEGER;    { Horiz. position of score. }
  166.         statsH: INTEGER;       { Horiz. position of text 'Enemy left:' }
  167.         statsNumH: INTEGER;
  168.         statsRect: Rect;
  169.         eLeft: INTEGER;        { How many enemy missiles are left this round. }
  170.         yLeft: INTEGER;        { How many missiles you (the player) have left. }
  171.         eDestroyed: INTEGER;   { Number of enemy missiles destroyed. }
  172.         eDesH: INTEGER;        { Horiz. position of text 'Enemy Destroyed:' }
  173.         eDesNumH: INTEGER;     { Horiz. position of # of enemy missiles destroyed. }
  174.  
  175.         gameSpeed: INTEGER;    { Number of ticks between each AdvanFb call. }
  176.         StartRound: INTEGER;   { Round to start off with. }
  177.         mFlag1: BOOLEAN;       { Do missiles aim for dead cities? }
  178.         MFlag2: INTEGER;       { Extent to which enemy missiles blow up. }
  179.         MExists: array[1..3] of BOOLEAN;  { Does this type of missile occur? }
  180.         MPBase: array[1..3] of INTEGER;  { Value of missile at first round... }
  181.         MPExtra: array[1..3] of INTEGER;  { ...plus this much each additional round... }
  182.         MPoints: array[1..3] of INTEGER;  { ...is this much in all. }
  183.  
  184. { %subtitle 'Init routine for menus.' }
  185.     procedure SetUpMenus;
  186.         var
  187.             i: INTEGER;
  188.     begin
  189.         myMenus[1] := GetMenu(appleMenu);             { Load menu from disk. }
  190. {MyMenus[1]^^.MenuData[1] := Chr(AppleSymbol); }
  191. { Change title to the Apple symbol. }
  192.         AddResMenu(myMenus[1], 'DRVR');                { Add items for desk accessories. }
  193.         myMenus[2] := GetMenu(fileMenu);
  194.         myMenus[3] := GetMenu(editMenu);
  195.         myMenus[4] := GetMenu(GameMenu);
  196.         for i := 1 to lastMenu do
  197.             InsertMenu(myMenus[i], 0);
  198.         DrawMenuBar;
  199.     end;    { of SetUpMenus }
  200.  
  201. { %subtitle 'Init routine for fireball BitMaps.' }
  202. {     This routine is called by the main Init routine, below.  It creates a}
  203. {    bunch of bit images of circles off-screen, which can later be drawn on}
  204. {    the screen much faster than FrameOval. }
  205.     procedure InitFbs;
  206.         var
  207.             i: FIXED;
  208.             aRect: Rect;           { Rectangle for drawing FrameOvals. }
  209.             saveBits: BitMap;      { To save the current GrafPort while drawing off-screen. }
  210.             SaveRect: Rect;
  211.     begin
  212.         SaveBits := ThePort^.PortBits;
  213.         SaveRect := ThePort^.PortRect;
  214.         fbBitMap.rowBytes := 8;
  215.         SetRect(fbBitMap.bounds, 0, 0, 60, 60);
  216.         ThePort^.portRect := fbBitMap.Bounds;
  217.  
  218.         PenSize(fbRadRate, fbRadRate);
  219.         PenPat(black);
  220.         PenMode(PatCopy);
  221.         SetRect(aRect, 30, 30, 30, 30);  { Start with an empty rectangle in the center. }
  222.         for i := 0 to 14 do
  223.             begin
  224.                 fbBitMap.baseAddr := @fbBits[240 * i + 1];    { Pick starting address for this picture. }
  225.                 insetRect(aRect, -2, -2);                   { Make rect a bit larger. }
  226.                 SetPortBits(fbBitMap);
  227.                 EraseRect(thePort^.portBits.bounds);        { Erase to all white. }
  228.                 FrameOval(aRect);                           { Draw the circle. }
  229.             end;
  230.         SetPortBits(SaveBits);          { Return to normal screen drawing. }
  231.         ThePort^.PortRect := SaveRect;
  232.     end;  { of InitFbs }
  233.  
  234. { %subtitle 'Init routine' }
  235.     procedure Setup;
  236.         var
  237.             wRect: Rect;  { Window Rectangle. }
  238.             i: INTEGER;
  239.     begin
  240.                         {     Macintosh System initialization. }
  241.         InitGraf(@thePort);   { Quickdraw. }
  242.         InitFonts;            { Font Manager. }
  243.         InitWindows;          { Window Manager. }
  244.         InitMenus;            { Menu Manager. }
  245.         TEInit;               { TextEdit. }
  246.         InitDialogs(nil);     { Dialog manager. }
  247.         InitCursor;           { Cursor handler. }
  248.         InitAllPacks;         { Package Manager. }
  249.  
  250.         SetUpMenus;           {   My routine to insert the menus. }
  251.         hCurs := POINTER(GetCursor(256));     { Load the Crosshairs cursor that I use in the game. }
  252.         hCurs2 := POINTER(GetCursor(257));     { Load the Crosshairs cursor that I use in the game. }
  253.         crosshairs := hCurs^^;
  254.         crosshairs2 := hCurs2^^;
  255.         SetCursor(crosshairs);
  256.         CityPattern := GetPattern(256);
  257.  
  258.         screenRect := screenBits.bounds;      { Don't actually use this, but might later. }
  259.  
  260.         doneFlag := FALSE;                    { This flag is set to False when user selects 'Quit'. }
  261.         HighScore := 0;
  262.  
  263.         GameSpeed := 8;                       { Set up all the user-settable options. }
  264.         StartRound := 0;
  265.         MFlag1 := True;
  266.         MFlag2 := 1;
  267.         for i := 1 to 3 do
  268.             begin
  269.                 MExists[i] := True;
  270.                 MPBase[i] := 10 * i;
  271.                 MPExtra[i] := 10 * i;
  272.             end;
  273.  
  274.   { myWindow := GetNewWindow(256, @wRecord, POINTER(-1)); }
  275.         wRect := ScreenRect;
  276.         wRect.top := 20;
  277.         myWindow := NewWindow(@wRecord, wRect, 'Missile Command   by Robert Munafo', True, 0, POINTER(-1), False, 0);                 { BoundsRect }
  278.                   { Visible }
  279.                      { ProcID }
  280.            { behind }
  281.                  { GoAwayFlag }
  282.                     { RefCon }
  283.         SetPort(myWindow);                    { Might as well play the game in a window... }
  284.  
  285.         FieldWidth := MyWindow^.portRect.right;   { These variables are used by the game. }
  286.         FieldHeight := MyWindow^.portRect.bottom;
  287.  
  288.         TextFont(0);
  289.         TextFace([]);
  290.  
  291.         InitFbs;              { Set up the fireball BitMaps. }
  292.  
  293.         second_string := GetString(str_2_ID);               { Get strings from resource. }
  294.         first_string := GetString(str_1_ID);
  295.  
  296.         FlushEvents(everyEvent, 0);
  297.     end;
  298.  
  299. { %Subtitle 'Gets a text string' }
  300.  
  301. {    This routine conducts a dialog to get a text string from the}
  302. {   user.  The function value returned indicates whether the user stopped by entering}
  303. {   OK or cancel.  The ID is the dialog ID number to use and Text is the}
  304. {   text they typed in.  To get the text, we display the dialog window,}
  305. {   get the handle of the edit text and execute a small event loop wich waits}
  306. {   until one of the two buttons is pressed.  When done, we dispose of the dialogue}
  307. {   window, set the flag depending on the means of exiting and return the text.}
  308. {     The predefined dialogue window has the following items in it:}
  309. {}
  310. {Item Number              Item}
  311. {     1                   OK button, Enabled}
  312. {     2                   Cancel button, Enabled}
  313. {     3                   EditText area, Enabled}
  314. {     4                   StatText area, Disabled (Prompt)}
  315. {     5...                Anything else, Disabled}
  316. {*)
  317.  
  318. { the actual function has been deleted. }
  319.  
  320. { %subtitle '"About Missile" routine.' }
  321. { This routine is nearly identical to Jason Ansley's About_Windows routine. }
  322.     procedure AboutMissile;
  323.         var
  324.             item_chosen: integer;   {which botton was hit (I only have one so it doesn't really matter)}
  325.             item_type: integer;     {type of item in res. def. file (needed to pass as parameter)}
  326.             first_handle, second_handle: Handle; {handles to my items}
  327.             the_dialog: DialogPtr;  {a pointer to my dialog box}
  328.             box: rect;              {the display rectangle of the item}
  329.  
  330.     begin
  331.         the_dialog := GetNewDialog(box_ID, nil, pointer(-1)); {get dialog box from resource file}
  332.  
  333.         GetDItem(the_dialog, 2, item_type, first_handle, box);  {get text format info from file}
  334.         GetDItem(the_dialog, 3, item_type, second_handle, box);
  335.  
  336.         SetIText(first_handle, first_string^^); {set the lines of text into the box}
  337.         SetIText(second_handle, second_string^^);
  338.  
  339.         ModalDialog(nil, item_chosen); {operate the box}
  340.         DisposDialog(the_dialog); {get rid of the box now that I'm done with it}
  341.     end;
  342.  
  343. { %subtitle 'My own random number routine.' }
  344.     function Rnd (n: FIXED): FIXED;  { Function to generate a random integer from}
  345. {                                      1 to N. }
  346.     begin
  347.         Rnd := (ABS(Random) mod n) + 1;
  348.     end;
  349.  
  350. { %subtitle 'DrawNumber - converts number to string and draws it on the screen.' }
  351.     procedure DrawNumber (n: LONGINT);
  352.         var
  353.             s: Str255;
  354.     begin
  355.         NumToString(n, s);
  356.         DrawString(s);
  357.     end;
  358.  
  359. { %subtitle 'Update the score at the bottom of the screen.' }
  360.     procedure UpdScore;
  361.     begin
  362.         TextMode(SrcBic);
  363.         MoveTo(ScoreNumH, FieldHeight - 5);
  364.         TextSize(24);
  365.         DrawNumber(disScore);
  366.  
  367.         TextMode(SrcOr);
  368.         MoveTo(ScoreNumH, FieldHeight - 5);
  369.         DrawNumber(Score);
  370.         disScore := Score;
  371.  
  372.         EraseRect(statsRect);
  373.         TextMode(SrcOr);
  374.         MoveTo(statsNumH, FieldHeight - 20);
  375.         TextSize(12);
  376.         DrawNumber(eLeft);
  377.         MoveTo(statsNumH, FieldHeight - 5);
  378.         DrawNumber(yLeft);
  379.         TextMode(SrcCopy);
  380.         MoveTo(eDesNumH, FieldHeight - 20);
  381.         DrawNumber(eDestroyed);
  382.     end;
  383.  
  384. { %subtitle 'Print the score.' }
  385.     procedure DrawScore;
  386.     begin
  387.         TextMode(srcCopy);
  388.         MoveTo(ScoreH, FieldHeight - 14);
  389.         TextSize(12);
  390.         DrawString('Score: ');
  391.         MoveTo(ScoreNumH, FieldHeight - 5);
  392.         TextSize(24);
  393.         DrawNumber(Score);
  394.         disScore := Score;
  395.  
  396.         TextSize(12);
  397.         MoveTo(statsH, FIeldHeight - 20);
  398.         DrawString('Enemy left: ');
  399.         DrawNumber(eLeft);
  400.         MoveTo(statsH, FieldHeight - 5);
  401.         DrawString('Yours left: ');
  402.         MoveTo(statsNumH, FieldHeight - 5);
  403.         DrawNumber(yLeft);
  404.         MoveTo(eDesH, FieldHeight - 20);
  405.         DrawString('Destroyed: ');
  406.         MoveTo(eDesNumH, FieldHeight - 20);
  407.         DrawNumber(eDestroyed);
  408.     end;
  409.  
  410. { %subtitle 'Print round #, score, and # killed.' }
  411.     procedure BottomLine;
  412.     begin
  413.         ;
  414.         TextSize(12);
  415.         RoundH := 10 + StringWidth('Round: ');
  416.         ScoreH := RoundH + 2 * StringWidth('00 ');
  417.         ScoreNumH := ScoreH + StringWidth('Score: ');
  418.         eDesH := FieldWidth - StringWidth('Destroyed: 0000 ');
  419.         eDesNumH := FieldWidth - StringWidth('0000 ');
  420.         statsH := eDesH - StringWidth('Enemy left: 000  ');
  421.         statsNumH := eDesH - StringWidth('000  ');
  422.         SetRect(statsRect, statsNumH, FieldHeight - 30, eDesH - 2, FieldHeight);
  423.  
  424.         TextMode(srcCopy);
  425.         MoveTo(10, FieldHeight - 14);
  426.         DrawString('Round: ');
  427.         MoveTo(RoundH, FieldHeight - 5);
  428.         TextSize(24);
  429.         DrawNumber(RoundNumber);
  430.  
  431.         DrawScore;
  432.         MoveTo(eDesH, FieldHeight - 5);
  433.         DrawString('High Score: ');
  434.         DrawNumber(HighScore);
  435.     end;
  436.  
  437. { %subtitle 'Create a new fireball.' }
  438.     procedure AllocFb (location: Point);
  439.         var
  440.             i: INTEGER;
  441.             idlefb: INTEGER;
  442.     begin
  443.         idlefb := 0;
  444.         for i := 1 to maxfb do
  445.             if (fbs[i].mode = fbIdle) and (idlefb = 0) then
  446.                 idlefb := i;
  447.         if idlefb <> 0 then
  448.             begin
  449.                 SetRect(fbs[idlefb].bounds, location.h - 30, location.v - 30, location.h + 30, location.v + 30);
  450.                 fbs[idlefb].size := 0;
  451.                 fbs[idlefb].mode := fbGrow;
  452.             end;
  453.     end;  { of AllocFb }
  454.  
  455. { %subtitle 'Advance (animate) the fireballs.' }
  456.     procedure AdvanFb;
  457.         var
  458.             i: INTEGER;
  459.             aRect: Rect;
  460.             fbrect: Rect;          { Rectangle for drawing fireballs. }
  461.     begin
  462.         PenSize(fbRadRate, fbRadRate);
  463.         PenPat(black);
  464.         SetRect(aRect, 0, 0, 60, 60);
  465.         for i := 1 to maxfb do
  466.             begin
  467.                 if fbs[i].mode <> fbidle then
  468.                     case fbs[i].mode of
  469.  
  470.                         fbGrow: 
  471.                             begin
  472.                                 fbs[i].size := fbs[i].size + 1;
  473.                                 fbBitMap.baseAddr := @fbBits[240 * fbs[i].size - 239];  { Select a fireball picture. }
  474.                                 CopyBits(fbBitMap, thePort^.PortBits, aRect, fbs[i].bounds, SrcOr, nil);
  475.  
  476.                                 if fbs[i].size >= fbRad then
  477.                                     fbs[i].mode := fbShrink;
  478.                             end;
  479.  
  480.                         fbShrink: 
  481.                             begin
  482.                                 fbBitMap.baseAddr := @fbBits[240 * fbs[i].size - 239];  { Select the correct fireball picture. }
  483.                                 CopyBits(fbBitMap, thePort^.PortBits, aRect, fbs[i].bounds, SrcBic, nil);
  484.  
  485.                                 fbs[i].size := fbs[i].size - 1;
  486.                                 if fbs[i].size = 0 then
  487.                                     fbs[i].mode := fbIdle;
  488.                             end;
  489.  
  490.                     end;  { of mode case }
  491.             end;   { of loop }
  492.     end;   { of AdvanFb }
  493.  
  494. { %subtitle 'Create a new enemy missile.' }
  495.     procedure AllocMs (position: Point; city: INTEGER; kind: INTEGER);
  496.         var
  497.             i: INTEGER;
  498.             idleMs: INTEGER;
  499.             targetcity: integer;
  500.             targeth: integer;
  501.     begin
  502.         idlems := 0;
  503.         for i := 1 to maxMs do
  504.             if (missiles[i].mode = msIdle) and (idlems = 0) then
  505.                 idlems := i;
  506.         if idlems <> 0 then
  507.             begin
  508.                 i := idlems;
  509.                 missiles[i].start.v := position.v;    { Set coordinates for beginning of path. }
  510.                 missiles[i].start.h := position.h;
  511.                 missiles[i].pos.v := missiles[i].start.v * 16;  { Initial current position is }
  512.                 missiles[i].pos.h := missiles[i].start.h * 16;  {   at start of path.o}
  513.                 missiles[i].dv := msSpeed;        { Speed of missile. }
  514.  
  515.                 missiles[i].city := city;
  516.                 targeth := (((missiles[i].city * 2 - 1) * FieldWidth) div (2 * nCities)) * 16;
  517.                 missiles[i].dh := (targeth - (missiles[i].start.h * 16)) div (((endv - missiles[i].start.v) * 16) div missiles[i].dv);
  518.                 missiles[i].kind := kind;
  519.                 missiles[i].mode := msActive;
  520.                 nMissiles := nMissiles + 1;
  521.             end;
  522.     end;
  523.  
  524.  
  525.     function BlackPixel (aPoint: Point): BOOLEAN;
  526.     begin
  527.         aPoint.h := aPoint.h div 16;
  528.         aPoint.v := aPoint.v div 16;
  529.         BlackPixel := (GetPixel(aPoint.h, aPoint.v) and GetPixel(aPoint.h + 1, aPoint.v));
  530.     end;
  531.  
  532. { %subtitle 'Advance (animate) the enemy missiles.' }
  533.     procedure AdvanMs;
  534.         var
  535.             i, j: INTEGER;
  536.             newpos, newpixel: Point;
  537.             detonate: BOOLEAN;
  538.             Points: INTEGER;
  539.     begin
  540.         penMode(PatCopy);
  541.         for i := 1 to maxms do
  542.             begin
  543.                 if missiles[i].mode <> msIdle then
  544.                     begin
  545.                         newpos.v := missiles[i].pos.v + missiles[i].dv;
  546.                         newpos.h := missiles[i].pos.h + missiles[i].dh;
  547.                         newpixel.v := newpos.v div 16;
  548.                         newpixel.h := newpos.h div 16;
  549.  
  550.                         detonate := FALSE;
  551.                         if BlackPixel(newpos) or BlackPixel(missiles[i].pos) then
  552.                             detonate := TRUE;
  553.  
  554.                         PenSize(mWidth, mWidth);
  555.                         PenPat(gray);
  556.                         MoveTo(missiles[i].pos.h div 16, missiles[i].pos.v div 16);
  557.                         LineTo(newpixel.h, newpixel.v);
  558.                         missiles[i].pos := newpos;
  559.  
  560.                         if (missiles[i].kind = msMirv) and (newpixel.v > mirvHeight) then   { If it's a Mirv, }
  561.   {  and it's reached the right altitude, }
  562.                             begin                       {  then make lots of little missiles... }
  563.                                 for j := 1 to nCities do
  564.                                     if (j <> missiles[i].city) and (Rnd(100) < mirvNasty) then
  565.                                         allocMs(newpixel, j, msNormal);
  566.                                 missiles[i].kind := msNormal;
  567.                             end;
  568.  
  569.                         if (newpos.v div 16 >= endv) or detonate then   { If the missile has reached its target }
  570.            {   or hit a fireball, }
  571.                             begin
  572.                                 PenSize(mWidth + 2, mWidth + 2);
  573.                                 PenPat(white);
  574.                                 MoveTo(missiles[i].start.h - 1, missiles[i].start.v - 1);  { Erase the line. }
  575.                                 LineTo(newpixel.h - 1, newpixel.v - 1);
  576.  
  577.                                 missiles[i].mode := msIdle;  { turn off the missile, }
  578.                                 nMissiles := nMissiles - 1;
  579.  
  580.                                 if detonate then
  581.                                     begin
  582.                                         if (MFlag2 = 2) or ((MFlag2 = 1) and (Rnd(25) > RoundNumber)) then
  583.                                             allocFb(newpixel);           { Make a new fireball. }
  584.                                         eDestroyed := eDestroyed + 1;
  585.                                         Score := Score + MPoints[missiles[i].kind];
  586.                                         UpdScore;
  587.                                     end
  588.                                 else if cities[missiles[i].city] then
  589.                                     begin
  590.                                         allocfb(newpixel);
  591.                                         cities[missiles[i].city] := false;
  592.                                         citiesLeft := citiesLeft - 1;
  593.                                         if citiesLeft + bCities = 0 then
  594.                                             gameOver := true;
  595.                                     end;
  596.                             end;  { Of detonate routine. }
  597.                     end;  { Of THEN clause for this active missile. }
  598.  
  599.             end;  { of missile loop }
  600.     end;  { of AdvanMs }
  601.  
  602. { %subtitle 'Print a string in the center of the window.' }
  603.     procedure Centre (TheText: Str255; posV: INTEGER);
  604.     begin
  605.         MoveTo((FieldWidth - StringWidth(TheText)) div 2, posV);
  606.         DrawString(TheText);
  607.     end;
  608.  
  609. { %subtitle 'Clear the screen.' }
  610.     procedure ClearScreen;
  611.         var
  612.             aRect: Rect;
  613.     begin
  614.         aRect.top := 0;
  615.         aRect.left := 0;
  616.         aRect.bottom := FieldHeight;
  617.         aRect.right := FieldWidth;
  618.         FillRect(aRect, white);
  619.     end;
  620.  
  621. { %subtitle 'Draw a city.' }
  622.     procedure DrawCity (city: INTEGER);
  623.         var
  624.             CityLoc: Point;
  625.             bOffset: INTEGER;
  626.             aRect: Rect;
  627.     begin
  628.         ;
  629.         RandSeed := 2;  { Make each city look the same. }
  630.         cityloc.v := FieldHeight - CityHeight;
  631.         cityloc.h := ((city * 2 - 1) * FieldWidth) div (2 * nCities);
  632.         bOffset := -(CityWidth div 2);
  633.         while bOffset < (CityWidth div 2) do
  634.             begin
  635.                 aRect.left := cityloc.h + bOffset;
  636.                 aRect.top := cityloc.v - Rnd(BuildHeight);
  637.                 aRect.bottom := cityloc.v + 2;
  638.                 aRect.right := aRect.left + BuildWidth;
  639.                 FillRect(aRect, CityPattern^^);
  640.                 bOffset := bOffset + BuildWidth;
  641.             end;
  642.     end;
  643.  
  644. { %subtitle 'Draw cities and bottom line.' }
  645.     procedure DrawStuff;
  646.         var
  647.             i: INTEGER;
  648.             aRect: Rect;
  649.     begin
  650.         ClearScreen;
  651.  
  652.         for i := 1 to nCities do
  653.             if cities[i] then
  654.                 DrawCity(i);
  655.         PenPat(black);
  656.         RandSeed := TickCount;  { Randomize }
  657.  
  658.         BottomLine;
  659.     end;  { of DrawStuff }
  660.  
  661. { %subtitle 'MinMax - Convert Num to String with range checking. }
  662.     function MinMax (TheString: Str255; minimum: INTEGER; Maximum: INTEGER): INTEGER;
  663.         var
  664.             TheNumber: LONGINT;
  665.     begin
  666.         StringToNum(TheString, TheNumber);
  667.         if TheNumber < Minimum then
  668.             TheNumber := Minimum;
  669.         if TheNumber > Maximum then
  670.             TheNumber := Maximum;
  671.         MinMax := TheNumber;
  672.     end;
  673.  
  674. { %subtitle 'DoGameOptions - Does the big dialog box.' }
  675.     procedure DoGameOptions;
  676.         const           { Here are all the magic constants that go with that monster dialog box: }
  677.             OK = 1;            { OK button }
  678.             GSpeed = 5;        { Game Speed text box }
  679.             SRound = 7;        { Start Round text box }
  680.             MPB = 18;          { MPBase text boxes }
  681.             MPE = 24;          { MPExtra text boxes }
  682.             MF1 = 8;           { MFlag1 check box }
  683.             MF2 = 10;          { MFlag2 radio buttons }
  684.             MEX = 15;          { MExists check boxes }
  685.  
  686.         var
  687.             savePort: grafptr;          { For saving the GrafPort and restoring later. }
  688.             DStorage: DialogRecord;    { Storage for my dialog box. }
  689.             DPtr: DialogPtr;           { Pointer to my dialog box. }
  690.             ItemHit: integer;          { Item that was just hit by the user. }
  691.             TheType: integer;          { not used }
  692.             TheHandle: Handle;         { Temp. handle }
  693.             TheRect: Rect;             { not used. }
  694.             TheString: str255;         { Temp. string }
  695.             TheValue: INTEGER;         { Temp. integer value. }
  696.             i: INTEGER;                { Loop Variable. }
  697.             Station: INTEGER;          { Current setting of radio buttons. }
  698.     begin
  699.         DPtr := getNewDialog(box2_id, @DStorage, pointer(-1));  { Load the data from disk . . . }
  700.  
  701.         GetDItem(Dptr, GSpeed, theType, theHandle, theRect);   { Set up all the EditText boxes }
  702.         NumToString(GameSpeed, TheString);
  703.         SetIText(theHandle, TheString);
  704.         GetDItem(Dptr, SRound, theType, theHandle, theRect);
  705.         NumToString(StartRound, TheString);
  706.         SetIText(theHandle, TheString);
  707.  
  708.         for i := 0 to 2 do
  709.             begin
  710.                 GetDItem(Dptr, MPB + i, theType, theHandle, theRect);  { Text for points each missile is worth. }
  711.                 NumToString(MPBase[i + 1], TheString);
  712.                 SetIText(theHandle, TheString);
  713.                 GetDItem(Dptr, MPB + 3 + i, theType, theHandle, theRect);  { Text for plus sign. }
  714.                 TheString := '+';
  715.                 SetIText(theHandle, TheString);
  716.                 GetDItem(Dptr, MPE + i, theType, theHandle, theRect);  { Text for additional points each round }
  717.                 NumToString(MPExtra[i + 1], TheString);
  718.                 SetIText(theHandle, TheString);
  719.                 GetDItem(Dptr, MPE + 3 + i, theType, theHandle, theRect);  { text : '* round' }
  720.                 TheString := '* round';
  721.                 SetIText(theHandle, TheString);
  722.                 GetDItem(Dptr, MEX + i, theType, theHandle, theRect);  { Check boxes for types of missiles }
  723.                 TheValue := 0;
  724.                 if MExists[i + 1] then
  725.                     TheValue := 1;
  726.                 SetCtlValue(Pointer(TheHandle), TheValue);
  727.             end;
  728.  
  729.         for i := 0 to 2 do
  730.             begin     { Program the radio buttons. }
  731.                 GetDItem(Dptr, MF2 + i, theType, theHandle, theRect);
  732.                 TheValue := 0;
  733.                 if MFlag2 = i then
  734.                     TheValue := 1;
  735.                 SetCtlValue(Pointer(TheHandle), TheValue);
  736.             end;
  737.         Station := MF2 + MFlag2;
  738.  
  739.         GetDItem(Dptr, MF1, theType, theHandle, theRect);  { Set check box for MFlag1. }
  740.         TheValue := 0;
  741.         if MFlag1 then
  742.             TheValue := 1;
  743.         SetCtlValue(Pointer(TheHandle), TheValue);
  744.  
  745.         SelIText(DPtr, sRound, 0, 1000);     { Initial selection is StartRound. }
  746.  
  747.         repeat
  748.             ModalDialog(nil, ItemHit);  { Let them hit an item... }
  749.  
  750.             if ItemHit in [10, 11, 12] then  { Was it one of the radio buttons? }
  751.                 begin
  752.                     GetDItem(Dptr, Station, theType, theHandle, theRect);  { Get the old one, and turn it off. }
  753.                     SetCtlValue(Pointer(TheHandle), 0);
  754.                     Station := ItemHit;
  755.                     GetDItem(Dptr, Station, theType, theHandle, theRect);  { Turn this one on. }
  756.                     SetCtlValue(Pointer(TheHandle), 1);
  757.                 end;
  758.  
  759.             if ItemHit in [8, 15, 16, 17] then  { Was it a check box? }
  760.                 begin
  761.                     GetDItem(Dptr, ItemHit, TheType, TheHandle, TheRect);
  762.                     TheValue := 1 - GetCtlValue(Pointer(TheHandle));  { Find the value and invert it. }
  763.                     SetCtlValue(Pointer(TheHandle), TheValue);
  764.                 end;
  765.         until ItemHit in [1, 2];
  766.  
  767.         if itemHit = OK then
  768.             begin   { Have to get all the new values now! }
  769.                 GetDItem(Dptr, GSpeed, theType, theHandle, theRect);   { Set up all the EditText boxes }
  770.                 GetIText(theHandle, TheString);
  771.                 GameSpeed := MinMax(TheString, 1, 99);
  772.  
  773.                 GetDItem(Dptr, SRound, theType, theHandle, theRect);
  774.                 GetIText(theHandle, TheString);
  775.                 StartRound := MinMax(TheString, 1, 99);
  776.  
  777.                 for i := 0 to 2 do
  778.                     begin
  779.                         GetDItem(Dptr, MPB + i, theType, theHandle, theRect);  { Text for points each missile is worth. }
  780.                         GetIText(theHandle, TheString);
  781.                         MPBase[i + 1] := MinMax(TheString, -1000, 1000);
  782.  
  783.                         GetDItem(Dptr, MPE + i, theType, theHandle, theRect);  { Text for additional points each round }
  784.                         GetIText(theHandle, TheString);
  785.                         MPExtra[i + 1] := MinMax(TheString, -1000, 1000);
  786.  
  787.                         GetDItem(Dptr, MEX + i, theType, theHandle, theRect);  { Check boxes for types of missiles }
  788.                         if GetCtlValue(Pointer(TheHandle)) = 0 then
  789.                             MExists[i + 1] := False
  790.                         else
  791.                             MExists[i + 1] := True;
  792.                     end;
  793.  
  794.                 for i := 0 to 2 do
  795.                     begin     { radio buttons. }
  796.                         GetDItem(Dptr, MF2 + i, theType, theHandle, theRect);
  797.                         if GetCtlValue(Pointer(TheHandle)) = 1 then
  798.                             MFlag2 := i;
  799.                     end;
  800.  
  801.                 GetDItem(Dptr, MF1, theType, theHandle, theRect);  { MFlag1. }
  802.                 if GetCtlValue(Pointer(TheHandle)) = 0 then
  803.                     MFlag1 := False
  804.                 else
  805.                     MFlag1 := True;
  806.             end;     {if itemHit = OK}
  807.  
  808.         DisposDialog(DPtr);
  809.         GetPort(savePort);           {save whatever port was current}
  810.         SetPort(MyWindow);
  811.         InvalRect(ScreenRect);       {force the entire screen, including frame, to be redrawn}
  812.         SetPort(savePort);
  813.     end;        { of DoGameOptions }
  814.  
  815. { %subtitle 'DoCommand - Does all the menu commands.' }
  816.     procedure DoCommand (mResult: LongInt);
  817.         var
  818.             name: STR255;
  819.             theMenu, theItem: INTEGER;  { Menu and item numbers. }
  820.             dummy: BOOLEAN;
  821.     begin
  822.         theMenu := HiWord(mResult);
  823.         theItem := LoWord(mResult);
  824.         case theMenu of
  825.  
  826.             appleMenu:                  { About Missile and Desk Accessories. }
  827.                 if theItem = 1 then
  828.                     AboutMissile
  829.                 else
  830.                     begin
  831.                         GetItem(myMenus[1], theItem, name);
  832.                         refNum := OpenDeskAcc(name);
  833.                     end;
  834.  
  835.             fileMenu:      { There is currently only one option in this menu. }
  836.                 begin
  837.                     doneFlag := TRUE;
  838.                     PauseFlag := False;
  839.                     esFlag := False;
  840.                 end;
  841.  
  842.             EditMenu:      { Cut, copy, paste, and undo. }
  843.                 begin
  844.                     dummy := SystemEdit(theItem - 1);  { Can't cut and paste in game window. }
  845.                 end;    { of editMenu }
  846.  
  847.             GameMenu: 
  848.                 begin
  849.         { SetPort(myWindow); }
  850.                     case theItem of
  851.                         1: 
  852.                             PauseFlag := True;   { pause game }
  853.                         2: 
  854.                             PauseFlag := False;  { resume game }
  855.                         3: 
  856.                             ;                    { Empty line in menu. }
  857.                         4: 
  858.                             DoGameOptions;
  859.                         5: 
  860.                             begin                { New Game. }
  861.                                 GameOver := True;
  862.                                 EndDelay := 100;
  863.                                 esFlag := False;
  864.                                 PauseFlag := False;
  865.                             end;
  866.                     end;    { of item case }
  867.                 end;    { of editMenu }
  868.  
  869.         end;    { of menu case }
  870.         HiliteMenu(0);
  871.  
  872.     end;  { of DoCommand }
  873.  
  874. { %subtitle 'Routine to check for events.' }
  875.     procedure CheckEvents;
  876.         var
  877.             MousePoint: Point;
  878.             MouseCode: INTEGER;
  879.             TheChar: CHAR;
  880.             i: FIXED;
  881.             fbrect: Rect;          { Rectangle for drawing fireballs. }
  882.     begin
  883.         repeat
  884.             SystemTask;
  885.  
  886.             GetMouse(MousePoint);
  887.             LocalToGlobal(MousePoint);
  888.             MouseCode := FindWindow(MousePoint, whichWindow);
  889.             if FrontWindow = MyWindow then  {  If my window is activated, set cursor: }
  890.                 if (WhichWindow = MyWindow) and (Mousecode <> inMenuBar) and (not PauseFlag) and (FrontWindow = MyWindow) and (yLeft > 0) then  { If it's above my window, }
  891.   {  but not the scroll bar, }
  892.            {  the game is 'active',   }
  893.   {  my window is in front,  }
  894.                {  and the user has missiles left, then: }
  895.                     SetCursor(crosshairs)  { Set the Missile Command cursor. }
  896.                 else
  897.                     SetCursor(crosshairs2);      { Otherwise, use an arrow. }
  898.  
  899.             while GetNextEvent(everyEvent, myEvent) do
  900.                 case myEvent.what of
  901.  
  902.                     mouseDown: 
  903.                         begin
  904.                             code := FindWindow(myEvent.where, whichWindow);
  905.                             case code of
  906.  
  907.                                 inMenuBar: 
  908.                                     DoCommand(MenuSelect(myEvent.where));
  909.  
  910.                                 inSysWindow: 
  911.                                     SystemClick(myEvent, whichWindow);
  912.  
  913.                                 inDrag: 
  914.                                     ;  { Can't drag game window - it's too hard to}
  915. {                         figure out collisions off-screen. }
  916.  
  917.                                 inGrow, inContent: 
  918.                                     begin
  919.                                         if (whichWindow <> FrontWindow) and (WhichWindow <> MyWindow) then  { Can't select MyWindow - must close other windows. }
  920.                                             SelectWindow(whichWindow)
  921.                                         else if (not PauseFlag) and (FrontWindow = MyWindow) and (yLeft > 0) then  { Can't make fireballs while paused. }
  922.  { Game must be running }
  923.      { Make sure they have some missiles left. }
  924.                                             begin
  925.                                                 GlobalToLocal(myEvent.where);
  926.                                                 if myEvent.where.v > NukeHeight then      { If it's too low, }
  927.                                                     myEvent.where.v := NukeHeight;  {   make it legal. }
  928.                                                 AllocFb(myEvent.where);      { Put a fireball in the list. }
  929.                                                 yLeft := yLeft - 1;       { They have less missiles now... }
  930.                                                 updScore;                 { let them know. }
  931.                                             end;
  932.                                     end;
  933.  
  934.                             end;    { of code case }
  935.                         end;    { of mouseDown }
  936.  
  937.                     keyDown:  { No Autokey - don't want to repeat Option-N. }
  938.                         if ((MyEvent.modifiers div 256) mod 2 <> 0) then  { If command key was held down }
  939.                             begin
  940.                                 TheChar := CHR(myEvent.message mod 256);
  941.                                 DoCommand(MenuKey(TheChar));
  942.                             end;
  943.  
  944.                     updateEvt: 
  945.                         begin
  946.                             SetPort(myWindow);
  947.                             BeginUpdate(myWindow);
  948.  
  949.                             if Playing then
  950.                                 begin
  951.                                     for i := 1 to nCities do
  952.                                         if cities[i] then
  953.                                             begin
  954.                                                 DrawCity(i);
  955.                                             end;
  956.                                     RandSeed := TickCount;  { Randomize after drawing cities. }
  957.  
  958.                                     for i := 1 to maxMs do
  959.                                         if missiles[i].mode = msActive then
  960.                                             begin
  961.                                                 PenSize(mWidth, mWidth);
  962.                                                 PenPat(Gray);
  963.                                                 MoveTo(missiles[i].start.h, missiles[i].start.v);
  964.                                                 LineTo(missiles[i].pos.h div 16, missiles[i].pos.v div 16);
  965.                                             end;
  966.  
  967.                                     for i := 1 to maxFb do
  968.                                         if fbs[i].mode <> fbIdle then
  969.                                             begin
  970.                                                 fbRect := fbs[i].bounds;
  971.                                                 insetRect(fbRect, 30 - 2 * fbs[i].size, 30 - 2 * fbs[i].size);
  972.                                                 PenMode(PatCopy);
  973.                                                 FillOval(fbRect, Black);
  974.                                             end;
  975.                                 end;
  976.  
  977.                             BottomLine;
  978.  
  979.                             EndUpdate(myWindow);
  980.                         end;    { of updateEvt }
  981.  
  982.                 end;    { of event case }
  983.  
  984.         until (FrontWindow = MyWindow) and not PauseFlag;   { If another window has been selected,      }
  985.            {   don't do anything except process events }
  986.                                    {   until game window is re-selected.       }
  987.     end;  { of CheckEvents }
  988.  
  989. { %subtitle 'Print the GAME OVER message.' }
  990.     procedure EndScreen;
  991.         var
  992.             aRect: Rect;
  993.             i: INTEGER;
  994.             GameOffset, OverOffset: INTEGER;
  995.             Center: Point;
  996.     begin
  997.         TextSize(72);
  998.         TextMode(srcBic);
  999.         PenMode(patOr);
  1000.         PenPat(Black);
  1001.         PenSize(5, 5);
  1002.  
  1003.         GameOffset := StringWidth('GAME') div 2;
  1004.         OverOffset := StringWidth('OVER') div 2;
  1005.         Center.v := FieldHeight div 2;
  1006.         Center.h := FieldWidth div 2;
  1007.  
  1008.         SetRect(aRect, Center.h, Center.v, Center.h, Center.v);
  1009.         for i := 1 to 30 do
  1010.             begin
  1011.                 CheckEvents;
  1012.  
  1013.                 InsetRect(aRect, -5, -5);
  1014.                 FrameOval(aRect);
  1015.                 TextSize(72);
  1016.                 TextMode(srcBic);
  1017.                 Centre('GAME', Center.v - 12);
  1018.                 Centre('OVER', Center.v + 60);
  1019.             end;
  1020.  
  1021.         PenMode(patBic);
  1022.         for i := 1 to 30 do
  1023.             begin
  1024.                 CheckEvents;
  1025.  
  1026.                 FrameOval(aRect);
  1027.                 InsetRect(aRect, 5, 5);
  1028.             end;
  1029.  
  1030.     end;  { of EndScreen }
  1031.  
  1032. { %subtitle 'Play a single game.' }
  1033.     procedure PlayGame;
  1034.         var
  1035.             i: INTEGER;
  1036.             s: Str255;
  1037.             msPoint: Point;
  1038.             msCity, msKind: INTEGER;
  1039.             rMissiles: INTEGER;      { "round missiles" - number to set eLeft to each round. }
  1040.             yMissiles: INTEGER;      { "your Missiles" - similar to rMissiles. }
  1041.             RoundOver: BOOLEAN;
  1042.             aString: Str255;
  1043.             Points: INTEGER;
  1044.  
  1045.         procedure InitRound;
  1046.             var
  1047.                 i: INTEGER;
  1048.         begin
  1049.             RoundOver := False;
  1050.             endDelay := 0;
  1051.             Playing := True;
  1052.  
  1053.             DrawStuff;  { Redraw screen. }
  1054.  
  1055.             for i := 1 to maxfb do
  1056.                 fbs[i].mode := fbIdle;
  1057.  
  1058.             for i := 1 to maxms do
  1059.                 missiles[i].mode := msIdle;
  1060.  
  1061.             for i := 1 to 3 do
  1062.                 MPoints[i] := MPBase[i] + MPExtra[i] * RoundNumber;
  1063.  
  1064.             for i := 1 to nCities do
  1065.                 cities2[i] := cities[i];
  1066.  
  1067.             nMissiles := 0;
  1068.  
  1069.             MirvRate := 0;
  1070.             mirvHeight := FieldHeight div 4;
  1071.             mirvNasty := 30;
  1072.             msSpeed := 60;
  1073.             if RoundNumber > 2 then
  1074.                 MirvRate := 10;
  1075.             if RoundNumber > 4 then
  1076.                 msSpeed := 80;
  1077.             if RoundNumber > 6 then
  1078.                 MirvRate := 20;
  1079.             if RoundNumber > 8 then
  1080.                 MirvNasty := 50;
  1081.             if RoundNumber > 10 then
  1082.                 MirvHeight := FieldHeight div 3;
  1083.             if RoundNumber > 12 then
  1084.                 msSpeed := 90;
  1085.             if RoundNumber > 14 then
  1086.                 MirvRate := 30;
  1087.             if RoundNumber > 19 then
  1088.                 msSpeed := 100;
  1089.  
  1090.             msRate := 5 + RoundNumber;
  1091.             rMissiles := (RoundNumber * RoundNumber) div 6 + RoundNumber + 6;  { Compute # of enemy missiles }
  1092.             yMissiles := rMissiles + RoundNumber;            { Adjust yMissiles accordingly. }
  1093.             eLeft := rMissiles;
  1094.             yLeft := yMissiles;
  1095.             RoundNumber := RoundNumber + 1;
  1096.             if (RoundNumber mod 5) = 0 then
  1097.                 bCities := bCities + 1;
  1098.  
  1099.             BottomLine;
  1100.             FlushEvents(everyEvent, 0);  { Ignore unprocessed events from previous round. }
  1101.         end;
  1102.  
  1103.         procedure EndBonus;
  1104.             var
  1105.                 i: INTEGER;
  1106.         begin
  1107.             ;
  1108.             ClearScreen;
  1109.             BottomLine;
  1110.  
  1111.             TextSize(24);
  1112.             TextMode(srcCopy);
  1113.  
  1114.             aString := 'End of round   ';
  1115.             aString[15] := Chr(48 + (RoundNumber mod 10));
  1116.             if RoundNumber > 9 then
  1117.                 aString[14] := Chr(48 + (RoundNumber div 10));
  1118.  
  1119.             if CitiesLeft > 0 then
  1120.                 begin  { give bonus points for cities }
  1121.                     Centre(aString, 100);
  1122.                     Centre('Bonus:', 130);
  1123.                     Points := 0;
  1124.                     for i := 1 to nCities do
  1125.                         if (cities[i] and not DoneFlag) then
  1126.                             begin
  1127.                                 LastTick := TickCount;
  1128.  
  1129.                                 DrawCity(i);
  1130.                                 Points := Points + RoundNumber * 20;
  1131.                                 Score := Score + RoundNumber * 20;
  1132.                                 TextSize(24);
  1133.                                 NumtoString(Points, S);
  1134.                                 Centre(S, 160);
  1135.                                 updScore;
  1136.  
  1137.                                 repeat
  1138.                                     CheckEvents;
  1139.                                 until (TickCount >= LastTick + 30) or DoneFlag;
  1140.                             end
  1141.                 end;  { of bonus for cities. }
  1142.             if (bCities > 0) and (CitiesLeft < 6) then
  1143.                 begin  { Give a bonus city. }
  1144.                     LastTick := TickCount;
  1145.                     TextSize(24);
  1146.                     Centre('* Bonus City *', 190);
  1147.                     CitiesLeft := CitiesLeft + 1;
  1148.                     bCities := bCities - 1;
  1149.                     repeat
  1150.                         i := Rnd(6)
  1151.                     until (not Cities[i]);
  1152.                     Cities[i] := true;
  1153.                     repeat
  1154.                         CheckEvents;
  1155.                     until (TickCount >= LastTick + 60) or DoneFlag;
  1156.                 end  { of bonus city routine. }
  1157.         end;  { of EndBonus }
  1158.  
  1159.     begin
  1160.         RoundNumber := StartRound;  { Start out at this round. }
  1161.         Score := 0;
  1162.         eDestroyed := 0;
  1163.  
  1164.         for i := 1 to nCities do
  1165.             cities[i] := true;
  1166.         citiesLeft := nCities;
  1167.         bCities := 0;
  1168.  
  1169.         NukeHeight := FieldHeight - CityHeight - (BuildHeight div 2) - fbRad * fbRadRate;
  1170.         endv := FieldHeight - CityHeight - (BuildHeight div 2);
  1171.  
  1172.         GameOver := False;
  1173.         esFlag := True;
  1174.         lastTick := TickCount;
  1175.  
  1176.         repeat  { Repeat loop for playing rounds. }
  1177.             InitRound;
  1178.  
  1179.             repeat  { Repeat loop for animating objects in game. }
  1180.                 CheckEvents;
  1181.  
  1182.                 if TickCount >= lastTick + gameSpeed then  { If enough time has elapsed since the}
  1183. {                                                  last update, update objects on screen. }
  1184.                     begin
  1185.                         lastTick := TickCount;
  1186.                         if (eLeft <= 0) and (nMissiles = 0) then
  1187.                             RoundOver := True;
  1188.  
  1189.                         if RoundOver or GameOver then
  1190.                             endDelay := endDelay + 1
  1191.                         else if (rnd(100) < msRate) and (eLeft > 0) then
  1192.                             begin               { Launch an enemy missile every now and then. }
  1193.                                 eLeft := eLeft - 1;
  1194.                                 msPoint.v := 0;
  1195.                                 msPoint.h := Rnd(FieldWidth - 4);
  1196.                                 repeat
  1197.                                     msCity := Rnd(nCities);      { Pick a city. }
  1198.                                 until ((Rnd(3) = 1) and mFlag1) or cities2[msCity];  { Try again for most missiles if city was destroyed. }
  1199.  
  1200.                                 msKind := 0;
  1201.                                 if MExists[msNormal] then
  1202.                                     msKind := msNormal;
  1203.                                 if (rnd(100) <= MirvRate) and MExists[msMirv] then
  1204.                                     msKind := msMirv;
  1205.                                 if msKind = 0 then
  1206.                                     msKind := msNormal;
  1207.  
  1208.                                 AllocMs(msPoint, msCity, msKind);
  1209.                             end;
  1210.  
  1211.                         AdvanFb;        { Advance state of fireballs. }
  1212.                         AdvanMs;        { Advance position of missiles. }
  1213.                     end;
  1214.  
  1215.             until ((GameOver or RoundOver) and (endDelay > 30)) or doneFlag;
  1216.                           { End of loop to move objects on screen. }
  1217.  
  1218.             Playing := False;
  1219.             if esFlag and (CitiesLeft + bCities > 0) and not DoneFlag then
  1220.                 EndBonus;
  1221.  
  1222.         until GameOver or DoneFlag;  { End of loop to play rounds. }
  1223.  
  1224.         if Score > HighScore then
  1225.             HighScore := Score;
  1226.  
  1227.         if esFlag then
  1228.             EndScreen;
  1229.  
  1230.     end;  { of PlayGame }
  1231.  
  1232. { %subtitle 'Main program.' }
  1233. begin    { main program }
  1234.     SetUp;
  1235.  
  1236.     repeat
  1237.         PlayGame;
  1238.     until doneFlag;
  1239. end.